﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IslandInterloper.Engine;
using System.Xml.Linq;
using System.IO;

namespace IslandInterloper.ConsoleClient
{
    public class Client
    {
        const ConsoleColor menuColor = ConsoleColor.DarkCyan;
        const ConsoleColor titleColor = ConsoleColor.Blue;
        const ConsoleColor contentColor = ConsoleColor.Black;
        const ConsoleColor hotKeyColor = ConsoleColor.Magenta;
        private delegate void ClientStateDelegate();
        private Dictionary<AvatarState, ClientStateDelegate> clientStates = new Dictionary<AvatarState, ClientStateDelegate>();
        private IGame game;
        private bool running = false;
        private void InitializeClientStates()
        {
            clientStates.Add(AvatarState.Uninitialized, UninitializedState);
            clientStates.Add(AvatarState.Docked, DockedState);
            clientStates.Add(AvatarState.AtSea, AtSeaState);
        }
        public Client(string gameFile)
        {
            game = GameFactory.Create(gameFile);
            InitializeClientStates();
        }
        public Client()
        {
            game = GameFactory.Create();
            InitializeClientStates();
        }
        public void Run()
        {
            if (running) return;
            Console.WindowWidth = 80;
            Console.WindowHeight = 25;
            Console.BufferWidth = 80;
            Console.BufferHeight = 25;
            Console.BackgroundColor = ConsoleColor.White;
            Console.ForegroundColor = ConsoleColor.Black;
            Console.Clear();
            Console.Title = "Island Interloper - PlayDeez Games";
            running = true;
            while (running)
            {
                AvatarState avatarState = Result.GetState(game.DoCommand(Command.CreateGetAvatarInformationCommand()));
                if (clientStates.ContainsKey(avatarState))
                {
                    clientStates[avatarState]();
                }
                else
                {
                    Console.WriteLine();
                    Console.WriteLine("No state handler found for state '{0}'.  Exiting...", avatarState.ToString());
                    Console.ReadLine();
                    running = false;
                }
            }
        }
        private void WriteTitle(string title)
        {
            ConsoleColor oldColor = Console.ForegroundColor;
            Console.ForegroundColor = titleColor;
            Console.WriteLine();
            Console.WriteLine(title);
            Console.ForegroundColor = oldColor;
        }
        private void WriteContent(string content)
        {
            ConsoleColor oldColor = Console.ForegroundColor;
            Console.ForegroundColor = contentColor;
            Console.WriteLine(content);
            Console.ForegroundColor = oldColor;
        }
        private void WriteMenu(List<string> menu)
        {
            ConsoleColor oldColor = Console.ForegroundColor;
            Console.ForegroundColor = menuColor;
            foreach (string menuItem in menu)
            {
                bool hotKey = false;
                foreach (char character in menuItem)
                {
                    if (character == '[')
                    {
                        hotKey = true;
                    }
                    if (hotKey)
                    {
                        Console.ForegroundColor = hotKeyColor;
                        Console.Write(character);
                    }
                    else
                    {
                        Console.ForegroundColor = menuColor;
                        Console.Write(character);
                    }
                    if (character == ']')
                    {
                        hotKey = false;
                    }
                }
                Console.WriteLine();
            }
            Console.ForegroundColor = oldColor;
        }
        private ConsoleKey ValidateInput(HashSet<ConsoleKey> possibleValues)
        {
            ConsoleKeyInfo key;
            do
            {
                key = Console.ReadKey(true);
            } while (!possibleValues.Contains(key.Key));
            return key.Key;
        }
        private bool PromptYesNo(string title,string promptMessage)
        {
            HashSet<ConsoleKey> yesNo = new HashSet<ConsoleKey>();
            List<string> menu = new List<string>();

            WriteTitle(title);
            WriteContent(promptMessage);

            menu.Add("[Y]es");
            yesNo.Add(ConsoleKey.Y);

            menu.Add("[N]o");
            yesNo.Add(ConsoleKey.N);

            WriteMenu(menu);

            return ValidateInput(yesNo) == ConsoleKey.Y;
        }
        const string saveSlotFileNameFormatString = "saveslot{0}.xml";
        private void ListSlotFiles(bool showEmptySlots,ref HashSet<ConsoleKey> result, ref List<string>  menu)
        {
            for (int slot = 0; slot < 10; ++slot)
            {
                string saveSlotFileName = string.Format(saveSlotFileNameFormatString, slot);
                if (File.Exists(saveSlotFileName))
                {
                    result.Add(ConsoleKey.D0 + slot);
                    result.Add(ConsoleKey.NumPad0 + slot);
                    DateTime lastTouched = File.GetLastWriteTime(saveSlotFileName);
                    menu.Add(string.Format("[{0}] - {1}", slot, lastTouched.ToString()));
                }
                else if(showEmptySlots)
                {
                    result.Add(ConsoleKey.D0 + slot);
                    result.Add(ConsoleKey.NumPad0 + slot);
                    menu.Add(string.Format("[{0}] - empty", slot));
                }
            }
        }
        private bool LoadGameFromSlot(int slot)
        {
            string saveSlotFileName = string.Format(saveSlotFileNameFormatString, slot);
            if (File.Exists(saveSlotFileName))
            {
                game = GameFactory.Create(saveSlotFileName);
                Console.WriteLine("Game loaded!");
                return true;
            }
            return false;
        }
        private bool SaveGameInSlot(int slot)
        {
            string saveSlotFileName = string.Format(saveSlotFileNameFormatString, slot);
            if (File.Exists(saveSlotFileName))
            {
                if (!PromptYesNo("Are you sure?",string.Format("Slot {0} already has data. Would you like to overwrite?", slot)))
                {
                    Console.WriteLine("Save cancelled!");
                    return false;
                }
            }
            GameFactory.Save(game, saveSlotFileName);
            Console.WriteLine("Game saved!");
            return true;
        }
        private void LoadGame()
        {
            bool done = false;
            while (!done)
            {
                WriteTitle("Choose a save slot:");
                HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
                List<string> menu = new List<string>();
                ListSlotFiles(false, ref keys, ref menu);
                menu.Add("[C]ancel");
                keys.Add(ConsoleKey.C);
                WriteMenu(menu);
                switch (ValidateInput(keys))
                {
                    case ConsoleKey.D0:
                    case ConsoleKey.NumPad0:
                        if (LoadGameFromSlot(0))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D1:
                    case ConsoleKey.NumPad1:
                        if (LoadGameFromSlot(1))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D2:
                    case ConsoleKey.NumPad2:
                        if (LoadGameFromSlot(2))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D3:
                    case ConsoleKey.NumPad3:
                        if (LoadGameFromSlot(3))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D4:
                    case ConsoleKey.NumPad4:
                        if (LoadGameFromSlot(4))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D5:
                    case ConsoleKey.NumPad5:
                        if (LoadGameFromSlot(5))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D6:
                    case ConsoleKey.NumPad6:
                        if (LoadGameFromSlot(6))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D7:
                    case ConsoleKey.NumPad7:
                        if (LoadGameFromSlot(7))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D8:
                    case ConsoleKey.NumPad8:
                        if (LoadGameFromSlot(8))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D9:
                    case ConsoleKey.NumPad9:
                        if (LoadGameFromSlot(9))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.C:
                        done = true;
                        break;
                }
            }
        }
        private void SaveGame()
        {
            bool done = false;
            while (!done)
            {
                WriteTitle("Choose a save slot:");
                HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
                List<string> menu = new List<string>();
                ListSlotFiles(true,ref keys,ref menu);
                menu.Add("[C]ancel");
                keys.Add(ConsoleKey.C);
                WriteMenu(menu);
                switch (ValidateInput(keys))
                {
                    case ConsoleKey.D0:
                    case ConsoleKey.NumPad0:
                        if (SaveGameInSlot(0))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D1:
                    case ConsoleKey.NumPad1:
                        if (SaveGameInSlot(1))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D2:
                    case ConsoleKey.NumPad2:
                        if (SaveGameInSlot(2))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D3:
                    case ConsoleKey.NumPad3:
                        if (SaveGameInSlot(3))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D4:
                    case ConsoleKey.NumPad4:
                        if (SaveGameInSlot(4))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D5:
                    case ConsoleKey.NumPad5:
                        if (SaveGameInSlot(5))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D6:
                    case ConsoleKey.NumPad6:
                        if (SaveGameInSlot(6))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D7:
                    case ConsoleKey.NumPad7:
                        if (SaveGameInSlot(7))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D8:
                    case ConsoleKey.NumPad8:
                        if (SaveGameInSlot(8))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.D9:
                    case ConsoleKey.NumPad9:
                        if (SaveGameInSlot(9))
                        {
                            done = true;
                        }
                        break;
                    case ConsoleKey.C:
                        done = true;
                        break;
                }
            }
        }
        private void MainMenu()
        {
            bool done = false;
            while (!done)
            {
                AvatarState avatarState = Result.GetState(game.DoCommand(Command.CreateGetAvatarInformationCommand()));
                WriteTitle("Main Menu:");
                HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
                List<string> menu = new List<string>();
                if (avatarState == AvatarState.Uninitialized)
                {
                    menu.Add("[N]ew");
                    menu.Add("[L]oad");
                    menu.Add("e[X]it");
                    keys.Add(ConsoleKey.N);
                    keys.Add(ConsoleKey.L);
                    keys.Add(ConsoleKey.X);
                }
                else
                {
                    menu.Add("[R]esume");
                    menu.Add("[S]ave");
                    menu.Add("[A]bandon");
                    keys.Add(ConsoleKey.S);
                    keys.Add(ConsoleKey.R);
                    keys.Add(ConsoleKey.A);
                }
                WriteMenu(menu);
                switch (ValidateInput(keys))
                {
                    case ConsoleKey.N:
                        game.DoCommand(Command.CreateStartGameCommand());
                        done = true;
                        break;
                    case ConsoleKey.R:
                        done = true;
                        break;
                    case ConsoleKey.S:
                        SaveGame();
                        break;
                    case ConsoleKey.L:
                        LoadGame();
                        break;
                    case ConsoleKey.A:
                        if (PromptYesNo("Are you sure?", "Are you sure you want to abandon?"))
                        {
                            game = GameFactory.Create();
                        }
                        break;
                    case ConsoleKey.X:
                        if (PromptYesNo("Are you sure?", "Are you sure you want to exit?"))
                        {
                            WriteTitle("Thanks for playing Island Interloper!");
                            running = false;
                            done = true;
                        }
                        break;
                }
            }
        }
        private void UninitializedState()
        {
            WriteTitle("Island Interloper");
            WriteContent("Another fine product of PlayDeez Games");
            MainMenu();
        }
        private void ShowInventory()
        {
            WriteTitle("Current Inventory:");
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            string[] commodities = Result.GetInventoryCommodities(avatarInfo);
            foreach (string commodity in commodities)
            {
                XElement commodityInfo = game.DoCommand(Command.CreateGetCommodityInformationCommand(commodity));
                WriteContent(string.Format("{0} {1:F2} Units @ {2:F2} (Tons: {3:F2})",
                    Result.GetName(commodityInfo),
                    Result.GetInventoryUnits(avatarInfo,commodity), 
                    Result.GetInventoryCostPerUnit(avatarInfo,commodity),
                    Result.GetInventoryTonnage(avatarInfo,commodity)));
            }
            WriteContent(string.Format("Total Tonnage: {0:F2}", Result.GetTotalTonnage(avatarInfo)));
        }
        private void ShowCrew()
        {
            WriteTitle("Crew:");
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            string[] crewmembers = Result.GetCrewList(avatarInfo);
            foreach (string crewmember in crewmembers)
            {
                XElement crewmemberInfo = game.DoCommand(Command.CreateGetCrewmemberInformationCommand(crewmember));
                WriteContent(string.Format("{0} {1}",Result.GetName(crewmemberInfo),Result.GetCrewCount(avatarInfo,crewmember)));
            }

        }
        private void ShowIslandInformation(int theIslandID)
        {
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(Result.GetDockedIsland(avatarInfo)));
            WriteTitle("Island Information:");
            if (Result.GetResult(islandInfo) != Result.ResultValue.Success)
            {
                WriteContent("Could not retrieve island information!");
                return;
            }
            WriteContent(string.Format("Information on '{0}':", Result.GetIslandName(islandInfo)));
            WriteContent(string.Format("You located this island {0} day(s) ago.", Result.GetDay(avatarInfo) - Result.GetDayLocated(islandInfo)));
            if (Result.GetVisited(islandInfo))
            {
                WriteContent(string.Format("You first visited this island {0} day(s) ago.", Result.GetDay(avatarInfo) - Result.GetDayFirstVisited(islandInfo)));
                WriteContent(string.Format("You last visited this island {0} day(s) ago.", Result.GetDay(avatarInfo) - Result.GetDayLastVisited(islandInfo)));
                WriteContent(string.Format("You have visited this island {0} time(s).", Result.GetVisitCount(islandInfo)));
                if (Result.GetBookmarked(islandInfo))
                {
                    WriteContent(string.Format("This island is bookmarked."));
                }
                if (Result.GetNotes(islandInfo) != string.Empty)
                {
                    WriteContent(string.Format("Annotation: {0}", Result.GetNotes(islandInfo)));
                }
            }
        }
        private void ShowPrices()
        {
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            if (Result.GetDockedIsland(avatarInfo) == -1) return;
            XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(Result.GetDockedIsland(avatarInfo)));
            WriteTitle("Prices:");
            string[] commodities = Result.GetIslandCommodities(islandInfo);
            foreach (string commodity in commodities)
            {
                XElement commodityInfo = game.DoCommand(Command.CreateGetCommodityInformationCommand(commodity));
                WriteContent(string.Format("{0} {1:F2} (Max Purchase: {2:F2})", Result.GetName(commodityInfo),Result.GetIslandCommodityBuyUnitPrice(islandInfo,commodity),Result.GetIslandCommodityMaximumPurchase(islandInfo,commodity)));
            }
        }
        private void Buy()
        {
            List<string> choices = new List<string>();
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            if (Result.GetDockedIsland(avatarInfo) == -1) return;
            XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(Result.GetDockedIsland(avatarInfo)));
            string[] commodities = Result.GetIslandCommodities(islandInfo);
            foreach (string commodity in commodities)
            {
                XElement commodityInfo = game.DoCommand(Command.CreateGetCommodityInformationCommand(commodity));
                choices.Add(string.Format("{0} {1:F2} (Max Purchase: {2:F2})", Result.GetName(commodityInfo), Result.GetIslandCommodityBuyUnitPrice(islandInfo, commodity), Result.GetIslandCommodityMaximumPurchase(islandInfo, commodity)));
            }
            int index = SelectFromPagedStringList("Buy Commodity:", choices.ToArray(), true);
            if (index != CancelResult)
            {
                double units;
                WriteContent("Enter Number of Units:");
                string input = Console.ReadLine();
                if (double.TryParse(input, out units) && units > 0.0)
                {
                    game.DoCommand(Command.CreateBuyCommodityCommand(commodities[index], units));
                    ShowInventory();
                }
            }
        }
        private void Sell()
        {
            List<string> choices = new List<string>();
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            if (Result.GetDockedIsland(avatarInfo) == -1) return;
            XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(Result.GetDockedIsland(avatarInfo)));
            string[] commodities = Result.GetInventoryCommodities(avatarInfo);
            foreach (string commodity in commodities)
            {
                XElement commodityInfo = game.DoCommand(Command.CreateGetCommodityInformationCommand(commodity));
                if (Result.GetTraded(commodityInfo))
                {
                    choices.Add(string.Format("{0} {1:F2}({2:F2}) (Units: {3:F2})", Result.GetName(commodityInfo), Result.GetIslandCommoditySellUnitPrice(islandInfo, commodity), Result.GetIslandCommoditySellUnitPrice(islandInfo, commodity) - Result.GetInventoryCostPerUnit(avatarInfo, commodity), Result.GetInventoryUnits(avatarInfo, commodity)));
                }
            }
            int index = SelectFromPagedStringList("Sell Commodity:", choices.ToArray(), true);
            if (index != CancelResult)
            {
                double units;
                WriteContent("Enter Number of Units:");
                string input = Console.ReadLine();
                if (double.TryParse(input, out units) && units > 0.0)
                {
                    game.DoCommand(Command.CreateSellCommodityCommand(commodities[index], units));
                    ShowInventory();
                }
            }
        }
        private void Market()
        {
            bool done = false;
            while (!done)
            {
                WriteTitle("Market:");
                HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
                List<string> menu = new List<string>();

                menu.Add("in[V]entory");
                keys.Add(ConsoleKey.V);

                menu.Add("[R]eturn to dock");
                keys.Add(ConsoleKey.R);

                menu.Add("[B]uy");
                keys.Add(ConsoleKey.B);

                menu.Add("[S]ell");
                keys.Add(ConsoleKey.S);

                menu.Add("[P]rices");
                keys.Add(ConsoleKey.P);

                WriteMenu(menu);
                switch (ValidateInput(keys))
                {
                    case ConsoleKey.R:
                        done = true;
                        break;
                    case ConsoleKey.V:
                        ShowInventory();
                        break;
                    case ConsoleKey.B:
                        Buy();
                        break;
                    case ConsoleKey.S:
                        Sell();
                        break;
                    case ConsoleKey.P:
                        ShowPrices();
                        break;
                }
            }
        }
        private void DockedState()
        {
            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(Result.GetDockedIsland(avatarInfo)));
            WriteTitle("Docked:");
            WriteContent(string.Format("Docked at '{0}'{1}{2}", Result.GetIslandName(islandInfo), Result.GetBookmarked(islandInfo) ? " [Bookmarked]" : "", Result.GetNotes(islandInfo) != string.Empty ? " (" + Result.GetNotes(islandInfo) + ")" : ""));


            HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
            List<string> menu = new List<string>();

            menu.Add("[M]ain menu");
            keys.Add(ConsoleKey.M);

            menu.Add("in[V]entory");
            keys.Add(ConsoleKey.V);

            menu.Add("cre[W]");
            keys.Add(ConsoleKey.W);

            menu.Add("mar[K]et");
            keys.Add(ConsoleKey.K);

            menu.Add("[T]avern");
            keys.Add(ConsoleKey.T);

            menu.Add("ship[Y]ard");
            keys.Add(ConsoleKey.Y);



            menu.Add(string.Format("[I]nformation on {0}", Result.GetIslandName(islandInfo)));
            keys.Add(ConsoleKey.I);

            menu.Add("[U]ndock");
            keys.Add(ConsoleKey.U);

            if (Result.GetBookmarked(islandInfo))
            {
                menu.Add("remove [B]ookmark");
            }
            else
            {
                menu.Add("[B]ookmark");
            }
            keys.Add(ConsoleKey.B);
            if (Result.GetNotes(islandInfo) != string.Empty)
            {
                menu.Add("change [A]nnotation");
            }
            else
            {
                menu.Add("[A]nnotate");
            }
            keys.Add(ConsoleKey.A);
            WriteMenu(menu);

            switch (ValidateInput(keys))
            {
                case ConsoleKey.M:
                    MainMenu();
                    break;
                case ConsoleKey.I:
                    ShowIslandInformation(Result.GetDockedIsland(avatarInfo));
                    break;
                case ConsoleKey.U:
                    game.DoCommand(Command.CreateUndockCommand());
                    break;
                case ConsoleKey.B:
                    game.DoCommand(Command.CreateToggleIslandBookmarkCommand(Result.GetDockedIsland(avatarInfo)));
                    break;
                case ConsoleKey.A:
                    if (Result.GetNotes(islandInfo) != string.Empty)
                    {
                        Console.WriteLine("Current annotation: {0}", Result.GetNotes(islandInfo));
                    }
                    Console.Write("Enter new annotation:");
                    string notes = Console.ReadLine();
                    game.DoCommand(Command.CreateAnnotateIslandCommand(Result.GetDockedIsland(avatarInfo), notes));
                    break;
                case ConsoleKey.V:
                    ShowInventory();
                    break;
                case ConsoleKey.W:
                    ShowCrew();
                    break;
                case ConsoleKey.K:
                    Market();
                    break;
                case ConsoleKey.T:
                    break;
                case ConsoleKey.Y:
                    break;
            }

        }
        const int CancelResult = -1;
        private int SelectFromPagedStringList(string title,string[] items,bool allowCancel)
        {
            int page = 0;
            int maximumPage = items.Length / 10;
            bool done = false;
            int result = CancelResult;
            while (!done)
            {
                WriteTitle(title);
                if (maximumPage != 0)
                {
                    WriteContent(string.Format("Page {0} of {0}", page + 1, maximumPage + 1));
                }
                List<string> menu = new List<string>();
                HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();

                int currentIndex = page * 10;
                for (int count = 0; count < 10 && currentIndex < items.Length; ++count)
                {
                    menu.Add(string.Format("[{0}] - {1}", count, items[currentIndex]));
                    keys.Add(ConsoleKey.D0 + count);
                    keys.Add(ConsoleKey.NumPad0 + count);
                    currentIndex++;
                }

                if (page > 0)
                {
                    menu.Add("[P]revious page");
                    keys.Add(ConsoleKey.P);
                }

                if (page < maximumPage)
                {
                    menu.Add("[N]ext page");
                    keys.Add(ConsoleKey.N);
                }

                if (allowCancel)
                {
                    menu.Add("[C]ancel");
                    keys.Add(ConsoleKey.C);
                }

                WriteMenu(menu);
                switch (ValidateInput(keys))
                {
                    case ConsoleKey.D0:
                    case ConsoleKey.NumPad0:
                        done = true;
                        result = 0 + page * 10;
                        break;
                    case ConsoleKey.D1:
                    case ConsoleKey.NumPad1:
                        done = true;
                        result = 1 + page * 10;
                        break;
                    case ConsoleKey.D2:
                    case ConsoleKey.NumPad2:
                        done = true;
                        result = 2 + page * 10;
                        break;
                    case ConsoleKey.D3:
                    case ConsoleKey.NumPad3:
                        done = true;
                        result = 3 + page * 10;
                        break;
                    case ConsoleKey.D4:
                    case ConsoleKey.NumPad4:
                        done = true;
                        result = 4 + page * 10;
                        break;
                    case ConsoleKey.D5:
                    case ConsoleKey.NumPad5:
                        done = true;
                        result = 5 + page * 10;
                        break;
                    case ConsoleKey.D6:
                    case ConsoleKey.NumPad6:
                        done = true;
                        result = 6 + page * 10;
                        break;
                    case ConsoleKey.D7:
                    case ConsoleKey.NumPad7:
                        done = true;
                        result = 7 + page * 10;
                        break;
                    case ConsoleKey.D8:
                    case ConsoleKey.NumPad8:
                        done = true;
                        result = 8 + page * 10;
                        break;
                    case ConsoleKey.D9:
                    case ConsoleKey.NumPad9:
                        done = true;
                        result = 9 + page * 10;
                        break;
                    case ConsoleKey.N:
                        page++;
                        break;
                    case ConsoleKey.P:
                        page--;
                        break;
                    case ConsoleKey.C:
                        result = CancelResult;
                        done = true;
                        break;
                }
            }
            return result;
        }
        private delegate string IslandListItemFormatterDelegate(int islandListItem);
        private string DockableIslandListItemFormatter(int islandListItem)
        {
            XElement result = game.DoCommand(Command.CreateGetIslandInformationCommand(islandListItem));
            return string.Format("{0}", Result.GetIslandName(result));
        }
        private string BoundForIslandListItemFormatter(int islandListItem)
        {
            XElement result = game.DoCommand(Command.CreateGetIslandInformationCommand(islandListItem));
            return string.Format("{0} - Distance {1:F2} Heading {2}", Result.GetIslandName(result), Result.GetIslandPosition(result).Magnitude, Result.GetIslandHeading(result));
        }
        private int SelectIslandFromIslandList(string title, int[] islandList, IslandListItemFormatterDelegate formatter, bool allowCancel)
        {
            List<string> items = new List<string>();
            foreach (int islandListItem in islandList)
            {
                items.Add(formatter(islandListItem));
            }
            int result = SelectFromPagedStringList(title, items.ToArray(), allowCancel);
            if (result == CancelResult)
            {
                return -1;
            }
            else
            {
                return islandList[result];
            }
        }
        private string SelectAvatarSpeed(string original)
        {
            List<string> choices = new List<string>();
            XElement avatarSpeedList = game.DoCommand(Command.CreateGetAvatarSpeedListCommand());
            string[] speeds = Result.GetAvatarSpeedList(avatarSpeedList);
            foreach (string speed in speeds)
            {
                XElement avatarSpeedInfo = game.DoCommand(Command.CreateGetAvatarSpeedInformationCommand(speed));
                choices.Add(Result.GetString(avatarSpeedInfo));
            }
            int newSpeed = SelectFromPagedStringList("Choose Speed:", choices.ToArray(), true);
            if (newSpeed == CancelResult)
            {
                return original;
            }
            else
            {
                return speeds[newSpeed];
            }
        }
        private void AtSeaState()
        {
            WriteTitle("At Sea:");

            HashSet<ConsoleKey> keys = new HashSet<ConsoleKey>();
            List<string> menu = new List<string>();

            menu.Add("[M]ain menu");
            keys.Add(ConsoleKey.M);

            menu.Add("in[V]entory");
            keys.Add(ConsoleKey.V);

            menu.Add("cre[W]");
            keys.Add(ConsoleKey.W);

            XElement islandList = game.DoCommand(Command.CreateGetDockableIslandListCommand());
            int[] dockableIslandList = Result.GetIslandList(islandList);
            switch (dockableIslandList.Length)
            {
                case 0://none... do nothing
                    break;
                case 1://one... dock by default here
                    WriteContent(string.Format("You can dock at {0}.", Result.GetIslandName(game.DoCommand(Command.CreateGetIslandInformationCommand(dockableIslandList[0])))));
                    menu.Add("[D]ock");
                    keys.Add(ConsoleKey.D);
                    break;
                default://two or more... shouldn't happen but if config gets changed we need to handle it
                    WriteContent("You can dock at multiple ports.");
                    menu.Add("[D]ock");
                    keys.Add(ConsoleKey.D);
                    break;
            }

            islandList = game.DoCommand(Command.CreateGetNearByIslandListCommand());
            int[] locatedIslandList = Result.GetIslandList(islandList);
            if (locatedIslandList.Length > 0)
            {
                WriteContent("Nearby islands:");
                foreach (int islandListItem in locatedIslandList)
                {
                    XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(islandListItem));
                    WriteContent(string.Format("{0} - Distance: {1:F2} Heading: {2}", Result.GetIslandName(islandInfo), Result.GetIslandPosition(islandInfo).Magnitude, Result.GetIslandHeading(islandInfo), true));
                }
            }

            XElement avatarInfo = game.DoCommand(Command.CreateGetAvatarInformationCommand());
            XElement avatarSpeedInfo = game.DoCommand(Command.CreateGetAvatarSpeedInformationCommand(Result.GetSpeed(avatarInfo)));
            WriteContent(string.Format("Current speed: {0}",Result.GetString(avatarSpeedInfo)));
            menu.Add("change s[P]eed");
            keys.Add(ConsoleKey.P);

            WriteContent(string.Format("Current heading: {0}", Result.GetHeading(avatarInfo)));
            menu.Add("change [H]eading");
            keys.Add(ConsoleKey.H);

            int boundFor = Result.GetBoundFor(avatarInfo);
            if (boundFor != -1)
            {
                XElement islandInfo = game.DoCommand(Command.CreateGetIslandInformationCommand(boundFor));
                WriteContent(string.Format("Bound for {0} - Distance: {1:F2}",Result.GetIslandName(islandInfo), Result.GetIslandPosition(islandInfo).Magnitude));
            }

            menu.Add("Head [F]or...");
            keys.Add(ConsoleKey.F);

            avatarSpeedInfo = game.DoCommand(Command.CreateGetAvatarSpeedInformationCommand(Result.GetSpeed(avatarInfo)));

            if (Result.GetDouble(avatarSpeedInfo)>0.0)
            {
                menu.Add("[S]ail");
                keys.Add(ConsoleKey.S);
            }

            WriteMenu(menu);
            switch (ValidateInput(keys))
            {
                case ConsoleKey.M:
                    MainMenu();
                    break;
                case ConsoleKey.D:
                    if (dockableIslandList != null)
                    {
                        switch (dockableIslandList.Length)
                        {
                            case 0://do nothing
                                break;
                            case 1:
                                game.DoCommand(Command.CreateDockCommand(dockableIslandList[0]));
                                break;
                            default:
                                int choice = SelectIslandFromIslandList("Choose Island For Docking:", dockableIslandList, DockableIslandListItemFormatter, true);
                                if (choice != CancelResult)
                                {
                                    game.DoCommand(Command.CreateDockCommand(choice));
                                }
                                break;
                        }
                    }
                    break;
                case ConsoleKey.P:
                    game.DoCommand(Command.CreateSetAvatarSpeedCommand(SelectAvatarSpeed(Result.GetSpeed(avatarInfo))));
                    break;
                case ConsoleKey.H:
                    string input;
                    int degrees;
                    do
                    {
                        WriteContent("Enter Degrees (0-359, no decimals):");
                        input = Console.ReadLine();
                    } while (!int.TryParse(input, out degrees) || degrees < 0 || degrees > 359);
                    int minutes;
                    do
                    {
                        WriteContent("Enter Minutes (0-59, no decimals):");
                        input = Console.ReadLine();
                    } while (!int.TryParse(input, out minutes) || minutes < 0 || minutes > 59);
                    double seconds;
                    do
                    {
                        WriteContent("Enter Seconds (0.0-59.999):");
                        input = Console.ReadLine();
                    } while (!double.TryParse(input, out seconds) || seconds < 0 || seconds >= 60);
                    game.DoCommand(Command.CreateSetHeadingCommand(degrees,minutes,seconds));
                    break;
                case ConsoleKey.S:
                    game.DoCommand(Command.CreateSailCommand());
                    break;
                case ConsoleKey.F:
                    XElement knownIslandList = game.DoCommand(Command.CreateGetKnownIslandListCommand());
                    int newBoundFor = SelectIslandFromIslandList("Select Island:", Result.GetIslandList(knownIslandList), BoundForIslandListItemFormatter, true);
                    game.DoCommand(Command.CreateSetBoundForIslandCommand(newBoundFor));
                    break;
                case ConsoleKey.V:
                    ShowInventory();
                    break;
                case ConsoleKey.W:
                    ShowCrew();
                    break;
            }
        }
    }
}
